/*
		ServEngine framework library
		A library to fast develop JEE online applications
	
		Copyright 2008 José Ignacio de Córdoba Álvaro

		Licensed under the Apache License, Version 2.0 (the "License");
		you may not use this file except in compliance with the License.
		You may obtain a copy of the License at

				http://www.apache.org/licenses/LICENSE-2.0

		Unless required by applicable law or agreed to in writing, software
		distributed under the License is distributed on an "AS IS" BASIS,
		WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
		See the License for the specific language governing permissions and
		limitations under the License.
	
		Jose Ignacio de Cordoba Alvaro
		http://ignacio.decordoba.com
*/

package com.servengine.util;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;
import java.util.Vector;

import javax.activation.DataHandler;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MailRelay implements Runnable
{
	private static Log log = LogFactory.getLog(MailRelay.class.getName());
	private String smtpserver;
	private String smtpauthid;
	private String smtpauthpassword;
	private int smtpport;
	protected javax.mail.Session session;
	protected Vector<MimeMessage> messageQueue;

	public MailRelay (ServletContext servletContext)
	{
/*		try
		{*/
		  smtpserver=servletContext.getInitParameter("MailRelay.smtpServer")==null?"localhost":servletContext.getInitParameter("MailRelay.smtpServer");
		  smtpauthid=servletContext.getInitParameter("MailRelay.smtpAuthId");
		  smtpauthpassword=servletContext.getInitParameter("MailRelay.smtpAuthPassword");
		  smtpport=servletContext.getInitParameter("MailRelay.smtpPort")==null?25:Integer.parseInt(servletContext.getInitParameter("MailRelay.smtpPort"));
		  Properties p = new Properties();
		  p.put("mail.smtp.host", this.smtpserver);
		  p.put("mail.smtp.port", smtpport);
		  p.put("mail.smtp.debug", "true");
		  if (smtpauthid == null)
		    session = Session.getInstance(p);
			else
			{
				p.put("mail.smtp.auth", "true");
				session = Session.getInstance(p,  new ForcedAuthenticator(smtpauthid, smtpauthpassword));
			  log.info("SMTPAUTH configured with id " + smtpauthid);
			}
		  log.info("Server smtp relay: " + this.smtpserver + ":" + smtpport);
		  messageQueue = new Vector<MimeMessage>();

/*		} catch(RuntimeException e)
		{
			log.error("ServletContextUtils: RuntimeException: ",e);
			throw e;
		}
		catch (Exception e)
		{
			log.error("ServletContextUtils: Exception: ",e);
			throw new IllegalStateException(e.getMessage());
		}*/
	}

	class ForcedAuthenticator extends Authenticator
	{
		String id, password;

		public ForcedAuthenticator(String id, String password)
		{
			this.id = id;
			this.password = password;
		}

		public PasswordAuthentication getPasswordAuthentication()
		{
			return new PasswordAuthentication(id, password);
		}
	}
	public void run()
	{
		if (log.isDebugEnabled())
			log.debug("Sending outgoing mails...");
	  for (MimeMessage message: messageQueue)
			try
			{
				sendMimeMessage(message);
			} catch (Exception e)
			{
				log.error(e.getMessage(), e);
			}
		if (log.isDebugEnabled())
			log.debug("Outgoing mails sent.");
	}
	private void sendMimeMessage(MimeMessage msg)
	{
		try
		{
	//      if (session != null)
			{
				if (smtpauthid == null)
					Transport.send(msg);
				else
				{
					msg.saveChanges(); // implicit with send(msg)
					if (log.isDebugEnabled())
					{
						log.debug("Using SMTPAUTH with id " + smtpauthid + " to send message id " + msg.getMessageID() + " from "+msg.getFrom()[0]+" to " +
								msg.getAllRecipients()[0]);
						try
						{
							log.debug(msg.getContent());
						} catch (IOException e)
						{
							log.error(e.getMessage(),e);
						}
					}
					Transport transport = session.getTransport("smtp");
					transport.connect(smtpserver, smtpport, smtpauthid, smtpauthpassword);
					transport.sendMessage(msg, msg.getAllRecipients());
					transport.close();
					if (log.isDebugEnabled())
						log.debug("Message id " + msg.getMessageID() + " sent");
				}
			} //else
				//MailQue.queMail(msg);
		} catch (MessagingException e)
		{
			try
			{
				if (log.isDebugEnabled())
					log.debug(e.getMessage(),e);
				log.warn(e+": "+e.getMessage()!=null?e.getMessage():""+", "+msg.getMessageID()+" ("+msg.getSubject()+")");
				Object[] recipientarray = msg.getAllRecipients();
				for (int i = 0; i < recipientarray.length; i++)
				  log.warn("Message  " + msg.getMessageID() + " NOT delivered to " + recipientarray[i]);
			} catch (Throwable e2)
			{
				log.error(e2.getMessage(), e2);
			}
		}
	}

	public void queue(InternetAddress from, InternetAddress to, String subject, String body) throws MessagingException
	{
		InternetAddress arrayto[] = { to };
		queue(from, arrayto, subject, body);
	}

	public void queue(String from, String to, String subject, String body) throws MessagingException
	{
		queue(new InternetAddress(from), new InternetAddress(to), subject, body);
	}

	public void queue(String from, String to, String subject, String body, String html, AttachmentDataSource attachment) throws MessagingException
	{
		if (attachment==null)
		{
			queue(from,to,subject,body,html);
		} else
		{
			Collection<AttachmentDataSource> attachments = new ArrayList<AttachmentDataSource>();
			attachments.add(attachment);
			queue(new InternetAddress(from), new InternetAddress(to), subject, body, html, attachments);
		}
	}

	public void queue(String from, String to, String subject, String body, String html, Collection<AttachmentDataSource> attachments) throws MessagingException
	{
		queue(new InternetAddress(from), new InternetAddress(to), subject, body, html, attachments);
	}

	public void queue(InternetAddress from, InternetAddress[] to, String subject, String body) throws MessagingException
	{
		if (log.isDebugEnabled())
			log.debug("About to create MimeMessage.");
		MimeMessage msg = new MimeMessage(session);
			
	//      session == null ? org.masukomi.aspirin.core.SimpleMimeMessageGenerator.getNewMimeMessage() : new MimeMessage(session);
		if (log.isDebugEnabled())
			log.debug("MimeMessage created: " + msg.getMessageID()+"; "+subject);
		msg.setFrom(from);
		msg.setRecipients(Message.RecipientType.TO, to);
		msg.setSubject(subject == null ? "" : subject);
		msg.setText(body == null ? "" : body);
		msg.setSentDate(new java.util.Date());
		messageQueue.add(msg);
	}

	public void queue(InternetAddress from, InternetAddress to, String subject, String body, String html, Collection<AttachmentDataSource> attachments) throws MessagingException
	{
		InternetAddress arrayto[] = { to };
		queue(from, arrayto, subject, body, html, attachments);
	}

	public void queue(String from, String to, String subject, String body, String html) throws MessagingException
	{
		queue(new InternetAddress(from), new InternetAddress(to), subject, body, html, null);
	}

	public int queue(InternetAddress from, InternetAddress[] to, String subject, String body, String html, Collection<AttachmentDataSource> attachments) throws MessagingException
	{
		if ((html == null || html.length() < 5) && (attachments == null || attachments.size() == 0)) // Deducimos que no debe haber cuerpo HTML
		{
			if (log.isDebugEnabled())
				log.debug("Will queue simple message with ho html nor attachments.");
			queue(from, to, subject, body);
			return to.length;
		}
		if (log.isDebugEnabled())
			log.debug("About to create MimeMessage.");
		MimeMessage msg = new MimeMessage(session);
		if (log.isDebugEnabled())
			log.debug("MimeMessage created: " + msg.getMessageID()+" (with "+(session==null?"Aspirin":"JavaMail SMTP")+")");
		msg.setFrom(from);
		msg.setRecipients(Message.RecipientType.TO, to);
		msg.setSubject(subject == null ? "" : subject);
		msg.setSentDate(new java.util.Date());

		Multipart mp = new MimeMultipart(); //Las partes que tendrá el mensaje (mixed)
		Multipart mpt = new MimeMultipart("alternative"); // Las subpartes del texto (alternative)

		MimeBodyPart mbp = new MimeBodyPart();
		if (body != null && body.length() > 0)
		{
			mbp = new MimeBodyPart();
			//      mbp.setContent(encodeText(body), "text/plain; charset=\"UTF-8\"");
			mbp.setText(body);
			mpt.addBodyPart(mbp);
		}
		if (html != null && html.length() > 5)
		{
			mbp = new MimeBodyPart();
			try
			{
				//mbp.setContent(html, "text/html; charset=\"UTF-8\"");
				mbp.setDataHandler(new DataHandler(new ByteArrayDataSource(html, "text/html; charset=\"UTF-8\"")));
			} catch (IOException e)
			{
				throw new MessagingException(e.getMessage(),e);
			}
			mpt.addBodyPart(mbp);
			if (log.isDebugEnabled())
				try
				{
					log.debug(mbp.getContent());
				}catch (java.io.IOException e)
				{
					log.error(e.getMessage(),e);
				}
		}
		mbp = new MimeBodyPart();
		mbp.setContent(mpt, "multipart/alternative");
		mp.addBodyPart(mbp);
		if (attachments != null)
		{
			for (AttachmentDataSource attachment: attachments)
			{//FIXME parece que falla si el vínculo tiene tamaño 0
				mbp = new MimeBodyPart();
				mbp.setDataHandler(new DataHandler(attachment));
				if (attachment.getName().equals("Forwarded message"))
					mbp.setDisposition(null);
				else
				{
					mbp.setFileName(attachment.getName());
					mbp.setDisposition(Part.ATTACHMENT);
				}
				mp.addBodyPart(mbp);
			}
		}
		msg.setContent(mp);
		messageQueue.add(msg);
		//if (log.isDebugEnabled())
			log.info("Message [" + msg.getSubject() + "] for " + msg.getAllRecipients()[0] + " queued");
		return to.length;
	}
/*	public void deliveryFailure(MimeMessage message, Collection recipients)
	{
		try
		{
			Iterator it = recipients.iterator();
			while (it.hasNext())
				log.warn("Message  " + message.getMessageID() + " NOT delivered to " + it.next());
		} catch (MessagingException e)
		{
			log.error(e);
		}
	}*/
	/**
	* Encodes text suitable for Mime format. Just invokes MimeUtility encoding. Under a java.io.UnsupportedEncodingException just returns back the text without any encoding.
	*/
/*	protected String encodeText(String text)
	{
		//    try
		//    { return MimeUtility.encodeText(text);
		//    } catch (java.io.UnsupportedEncodingException e)
		{
			return text;
		}
	}*/
	public void sendQueuedMail()
	{
	  new Thread(this).start();
	}
	public int getQueueSize()
	{
		return messageQueue.size();
	}
	public void send(String from, String to, String subject, String plainBody, String htmlBody) throws MessagingException
	{
		queue(from,to,subject,plainBody,htmlBody);
		new Thread(this).start();
	}
	public void send(String from, String to, String subject, String plainBody, String htmlBody,AttachmentDataSource attachment) throws MessagingException
	{
		queue(from,to,subject,plainBody,htmlBody,attachment);
		new Thread(this).start();
	}
	public void send(String from, String to, String subject, String plainBody, String htmlBody,Collection<AttachmentDataSource> attachments) throws MessagingException
	{
		queue(from,to,subject,plainBody,htmlBody,attachments);
		new Thread(this).start();
	}
}
